/*
 *  UVCCamera
 *  library and sample to access to UVC web camera on non-rooted Android device
 *
 * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 *  All files in the folder are under this Apache License, Version 2.0.
 *  Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
 *  may have a different license, see the respective files.
 */

package com.serenegiant.usbcameratest7;

import android.graphics.SurfaceTexture;
import android.hardware.usb.UsbDevice;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.Toast;

import com.serenegiant.common.BaseActivity;
import com.serenegiant.usb.CameraDialog;
import com.serenegiant.usb.USBMonitor;
import com.serenegiant.usb.USBMonitor.OnDeviceConnectListener;
import com.serenegiant.usb.USBMonitor.UsbControlBlock;
import com.serenegiant.usb.UVCCamera;
import com.serenegiant.usbcameracommon.UVCCameraHandler;
import com.serenegiant.widget.CameraViewInterface;
import com.serenegiant.widget.UVCCameraTextureView;

/**
 * Show side by side view from two camera.
 * You cane record video images from both camera, but secondarily started recording can not record
 * audio because of limitation of Android AudioRecord(only one instance of AudioRecord is available
 * on the device) now.
 */
public final class MainActivity extends BaseActivity implements CameraDialog.CameraDialogParent {
    private static final boolean DEBUG = false;    // FIXME set false when production
    private static final String TAG = "MainActivity";

    private static final float[] BANDWIDTH_FACTORS = {0.5f, 0.5f};

    // for accessing USB and USB camera
    private USBMonitor mUSBMonitor;

    private UVCCameraHandler mHandlerR;
    private CameraViewInterface mUVCCameraViewR;
    private ImageButton mCaptureButtonR;
    private Surface mRightPreviewSurface;

    private UVCCameraHandler mHandlerC;
    private CameraViewInterface mUVCCameraViewC;
    private ImageButton mCaptureButtonC;
    private Surface mCenterPreviewSurface;

    private UVCCameraHandler mHandlerL;
    private CameraViewInterface mUVCCameraViewL;
    private ImageButton mCaptureButtonL;
    private Surface mLeftPreviewSurface;

    private UVCCameraHandler mHandlerDR;
    private CameraViewInterface mUVCCameraViewDR;
    private ImageButton mCaptureButtonDR;
    private Surface mDownRightPreviewSurface;

    private UVCCameraHandler mHandlerDC;
    private CameraViewInterface mUVCCameraViewDC;
    private ImageButton mCaptureButtonDC;
    private Surface mDownCenterPreviewSurface;

    private UVCCameraHandler mHandlerDL;
    private CameraViewInterface mUVCCameraViewDL;
    private ImageButton mCaptureButtonDL;
    private Surface mDownLeftPreviewSurface;


    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.RelativeLayout1).setOnClickListener(mOnClickListener);
        mUVCCameraViewL = (CameraViewInterface) findViewById(R.id.camera_view_L);
        mUVCCameraViewL.setAspectRatio(UVCCamera.DEFAULT_PREVIEW_WIDTH / (float) UVCCamera.DEFAULT_PREVIEW_HEIGHT);
        ((UVCCameraTextureView) mUVCCameraViewL).setOnClickListener(mOnClickListener);
        mCaptureButtonL = (ImageButton) findViewById(R.id.capture_button_L);
        mCaptureButtonL.setOnClickListener(mOnClickListener);
        mCaptureButtonL.setVisibility(View.INVISIBLE);
        mHandlerL = UVCCameraHandler.createHandler(this, mUVCCameraViewL, UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, BANDWIDTH_FACTORS[0]);

        mUVCCameraViewC = (CameraViewInterface) findViewById(R.id.camera_view_C);
        mUVCCameraViewC.setAspectRatio(UVCCamera.DEFAULT_PREVIEW_WIDTH / (float) UVCCamera.DEFAULT_PREVIEW_HEIGHT);
        ((UVCCameraTextureView) mUVCCameraViewC).setOnClickListener(mOnClickListener);
        mCaptureButtonC = (ImageButton) findViewById(R.id.capture_button_C);
        mCaptureButtonC.setOnClickListener(mOnClickListener);
        mCaptureButtonC.setVisibility(View.INVISIBLE);
        mHandlerC = UVCCameraHandler.createHandler(this, mUVCCameraViewC, UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, BANDWIDTH_FACTORS[0]);

        mUVCCameraViewR = (CameraViewInterface) findViewById(R.id.camera_view_R);
        mUVCCameraViewR.setAspectRatio(UVCCamera.DEFAULT_PREVIEW_WIDTH / (float) UVCCamera.DEFAULT_PREVIEW_HEIGHT);
        ((UVCCameraTextureView) mUVCCameraViewR).setOnClickListener(mOnClickListener);
        mCaptureButtonR = (ImageButton) findViewById(R.id.capture_button_R);
        mCaptureButtonR.setOnClickListener(mOnClickListener);
        mCaptureButtonR.setVisibility(View.INVISIBLE);
        mHandlerR = UVCCameraHandler.createHandler(this, mUVCCameraViewR, UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, BANDWIDTH_FACTORS[0]);

        mUVCCameraViewDL = (CameraViewInterface) findViewById(R.id.camera_view_D_L);
        mUVCCameraViewDL.setAspectRatio(UVCCamera.DEFAULT_PREVIEW_WIDTH / (float) UVCCamera.DEFAULT_PREVIEW_HEIGHT);
        ((UVCCameraTextureView) mUVCCameraViewDL).setOnClickListener(mOnClickListener);
        mCaptureButtonDL = (ImageButton) findViewById(R.id.capture_button_D_L);
        mCaptureButtonDL.setOnClickListener(mOnClickListener);
        mCaptureButtonDL.setVisibility(View.INVISIBLE);
        mHandlerDL = UVCCameraHandler.createHandler(this, mUVCCameraViewDL, UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, BANDWIDTH_FACTORS[0]);

        mUVCCameraViewDC = (CameraViewInterface) findViewById(R.id.camera_view_D_C);
        mUVCCameraViewDC.setAspectRatio(UVCCamera.DEFAULT_PREVIEW_WIDTH / (float) UVCCamera.DEFAULT_PREVIEW_HEIGHT);
        ((UVCCameraTextureView) mUVCCameraViewDC).setOnClickListener(mOnClickListener);
        mCaptureButtonDC = (ImageButton) findViewById(R.id.capture_button_D_C);
        mCaptureButtonDC.setOnClickListener(mOnClickListener);
        mCaptureButtonDC.setVisibility(View.INVISIBLE);
        mHandlerDC = UVCCameraHandler.createHandler(this, mUVCCameraViewDC, UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, BANDWIDTH_FACTORS[0]);

        mUVCCameraViewDR = (CameraViewInterface) findViewById(R.id.camera_view_D_R);
        mUVCCameraViewDR.setAspectRatio(UVCCamera.DEFAULT_PREVIEW_WIDTH / (float) UVCCamera.DEFAULT_PREVIEW_HEIGHT);
        ((UVCCameraTextureView) mUVCCameraViewDR).setOnClickListener(mOnClickListener);
        mCaptureButtonDR = (ImageButton) findViewById(R.id.capture_button_D_R);
        mCaptureButtonDR.setOnClickListener(mOnClickListener);
        mCaptureButtonDR.setVisibility(View.INVISIBLE);
        mHandlerDR = UVCCameraHandler.createHandler(this, mUVCCameraViewDR, UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, BANDWIDTH_FACTORS[0]);

        mUSBMonitor = new USBMonitor(this, mOnDeviceConnectListener);
    }

    @Override
    protected void onStart() {
        super.onStart();
        mUSBMonitor.register();
        if (mUVCCameraViewR != null)
            mUVCCameraViewR.onResume();
        if (mUVCCameraViewC != null)
            mUVCCameraViewC.onResume();
        if (mUVCCameraViewL != null)
            mUVCCameraViewL.onResume();
        if (mUVCCameraViewDR != null)
            mUVCCameraViewDR.onResume();
        if (mUVCCameraViewDC != null)
            mUVCCameraViewDC.onResume();
        if (mUVCCameraViewDL != null)
            mUVCCameraViewDL.onResume();
    }

    @Override
    protected void onStop() {
        mHandlerR.close();
        if (mUVCCameraViewR != null)
            mUVCCameraViewR.onPause();
        mCaptureButtonR.setVisibility(View.INVISIBLE);
        mHandlerC.close();
        if (mUVCCameraViewC != null)
            mUVCCameraViewC.onPause();
        mCaptureButtonR.setVisibility(View.INVISIBLE);
        mHandlerL.close();
        if (mUVCCameraViewL != null)
            mUVCCameraViewL.onPause();
        mCaptureButtonL.setVisibility(View.INVISIBLE);
        mHandlerDR.close();
        if (mUVCCameraViewDR != null)
            mUVCCameraViewDR.onPause();
        mCaptureButtonDR.setVisibility(View.INVISIBLE);
        mHandlerDC.close();
        if (mUVCCameraViewDC != null)
            mUVCCameraViewDC.onPause();
        mCaptureButtonDR.setVisibility(View.INVISIBLE);
        mHandlerDL.close();
        if (mUVCCameraViewDL != null)
            mUVCCameraViewDL.onPause();
        mCaptureButtonDL.setVisibility(View.INVISIBLE);
        mUSBMonitor.unregister();
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        if (mHandlerR != null) {
            mHandlerR = null;
        }
        if (mHandlerC != null) {
            mHandlerC = null;
        }
        if (mHandlerL != null) {
            mHandlerL = null;
        }
        if (mHandlerDR != null) {
            mHandlerDR = null;
        }
        if (mHandlerDC != null) {
            mHandlerDC = null;
        }
        if (mHandlerDL != null) {
            mHandlerDL = null;
        }
        if (mUSBMonitor != null) {
            mUSBMonitor.destroy();
            mUSBMonitor = null;
        }
        mUVCCameraViewR = null;
        mCaptureButtonR = null;
        mUVCCameraViewC = null;
        mCaptureButtonC = null;
        mUVCCameraViewL = null;
        mCaptureButtonL = null;
        mUVCCameraViewDR = null;
        mCaptureButtonDR = null;
        mUVCCameraViewDC = null;
        mCaptureButtonDC = null;
        mUVCCameraViewDL = null;
        mCaptureButtonDL = null;
        super.onDestroy();
    }

    private final OnClickListener mOnClickListener = new OnClickListener() {
        @Override
        public void onClick(final View view) {
            switch (view.getId()) {
                case R.id.camera_view_L:
                    if (mHandlerL != null) {
                        if (!mHandlerL.isOpened()) {
                            CameraDialog.showDialog(MainActivity.this);
                        } else {
                            mHandlerL.close();
                            setCameraButton();
                        }
                    }
                    break;
                case R.id.camera_view_C:
                    if (mHandlerC != null) {
                        if (!mHandlerC.isOpened()) {
                            CameraDialog.showDialog(MainActivity.this);
                        } else {
                            mHandlerC.close();
                            setCameraButton();
                        }
                    }
                    break;
                case R.id.camera_view_R:
                    if (mHandlerR != null) {
                        if (!mHandlerR.isOpened()) {
                            CameraDialog.showDialog(MainActivity.this);
                        } else {
                            mHandlerR.close();
                            setCameraButton();
                        }
                    }
                    break;
                case R.id.camera_view_D_L:
                    if (mHandlerDL != null) {
                        if (!mHandlerDL.isOpened()) {
                            CameraDialog.showDialog(MainActivity.this);
                        } else {
                            mHandlerDL.close();
                            setCameraButton();
                        }
                    }
                    break;
                case R.id.camera_view_D_C:
                    if (mHandlerDC != null) {
                        if (!mHandlerDC.isOpened()) {
                            CameraDialog.showDialog(MainActivity.this);
                        } else {
                            mHandlerDC.close();
                            setCameraButton();
                        }
                    }
                    break;
                case R.id.camera_view_D_R:
                    if (mHandlerDR != null) {
                        if (!mHandlerDR.isOpened()) {
                            CameraDialog.showDialog(MainActivity.this);
                        } else {
                            mHandlerDR.close();
                            setCameraButton();
                        }
                    }
                    break;
                case R.id.capture_button_L:
                    if (mHandlerL != null) {
                        if (mHandlerL.isOpened()) {
                            if (checkPermissionWriteExternalStorage() && checkPermissionAudio()) {
                                if (!mHandlerL.isRecording()) {
                                    mCaptureButtonL.setColorFilter(0xffff0000);    // turn red
                                    mHandlerL.startRecording();
                                } else {
                                    mCaptureButtonL.setColorFilter(0);    // return to default color
                                    mHandlerL.stopRecording();
                                }
                            }
                        }
                    }
                    break;
                case R.id.capture_button_C:
                    if (mHandlerC != null) {
                        if (mHandlerC.isOpened()) {
                            if (checkPermissionWriteExternalStorage() && checkPermissionAudio()) {
                                if (!mHandlerC.isRecording()) {
                                    mCaptureButtonC.setColorFilter(0xffff0000);    // turn red
                                    mHandlerC.startRecording();
                                } else {
                                    mCaptureButtonC.setColorFilter(0);    // return to default color
                                    mHandlerC.stopRecording();
                                }
                            }
                        }
                    }
                    break;
                case R.id.capture_button_R:
                    if (mHandlerR != null) {
                        if (mHandlerR.isOpened()) {
                            if (checkPermissionWriteExternalStorage() && checkPermissionAudio()) {
                                if (!mHandlerR.isRecording()) {
                                    mCaptureButtonR.setColorFilter(0xffff0000);    // turn red
                                    mHandlerR.startRecording();
                                } else {
                                    mCaptureButtonR.setColorFilter(0);    // return to default color
                                    mHandlerR.stopRecording();
                                }
                            }
                        }
                    }
                    break;
                case R.id.capture_button_D_L:
                    if (mHandlerDL != null) {
                        if (mHandlerDL.isOpened()) {
                            if (checkPermissionWriteExternalStorage() && checkPermissionAudio()) {
                                if (!mHandlerDL.isRecording()) {
                                    mCaptureButtonDL.setColorFilter(0xffff0000);    // turn red
                                    mHandlerDL.startRecording();
                                } else {
                                    mCaptureButtonDL.setColorFilter(0);    // return to default color
                                    mHandlerDL.stopRecording();
                                }
                            }
                        }
                    }
                    break;
                case R.id.capture_button_D_C:
                    if (mHandlerDC != null) {
                        if (mHandlerDC.isOpened()) {
                            if (checkPermissionWriteExternalStorage() && checkPermissionAudio()) {
                                if (!mHandlerDC.isRecording()) {
                                    mCaptureButtonDC.setColorFilter(0xffff0000);    // turn red
                                    mHandlerDC.startRecording();
                                } else {
                                    mCaptureButtonDC.setColorFilter(0);    // return to default color
                                    mHandlerDC.stopRecording();
                                }
                            }
                        }
                    }
                    break;
                case R.id.capture_button_D_R:
                    if (mHandlerDR != null) {
                        if (mHandlerDR.isOpened()) {
                            if (checkPermissionWriteExternalStorage() && checkPermissionAudio()) {
                                if (!mHandlerDR.isRecording()) {
                                    mCaptureButtonDR.setColorFilter(0xffff0000);    // turn red
                                    mHandlerDR.startRecording();
                                } else {
                                    mCaptureButtonDR.setColorFilter(0);    // return to default color
                                    mHandlerDR.stopRecording();
                                }
                            }
                        }
                    }
                    break;
            }
        }
    };

    private final OnDeviceConnectListener mOnDeviceConnectListener = new OnDeviceConnectListener() {
        @Override
        public void onAttach(final UsbDevice device) {
            if (DEBUG) Log.v(TAG, "onAttach:" + device);
            Toast.makeText(MainActivity.this, "USB_DEVICE_ATTACHED", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onConnect(final UsbDevice device, final UsbControlBlock ctrlBlock, final boolean createNew) {
            if (DEBUG) Log.v(TAG, "onConnect:" + device);
            if (!mHandlerL.isOpened()) {
                mHandlerL.open(ctrlBlock);
                final SurfaceTexture st = mUVCCameraViewL.getSurfaceTexture();
                mHandlerL.startPreview(new Surface(st));
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mCaptureButtonL.setVisibility(View.VISIBLE);
                    }
                });
            } else if (!mHandlerC.isOpened()) {
                mHandlerC.open(ctrlBlock);
                final SurfaceTexture st = mUVCCameraViewC.getSurfaceTexture();
                mHandlerC.startPreview(new Surface(st));
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mCaptureButtonC.setVisibility(View.VISIBLE);
                    }
                });
            }else if (!mHandlerR.isOpened()) {
                mHandlerR.open(ctrlBlock);
                final SurfaceTexture st = mUVCCameraViewR.getSurfaceTexture();
                mHandlerR.startPreview(new Surface(st));
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mCaptureButtonR.setVisibility(View.VISIBLE);
                    }
                });
            }else if (!mHandlerDL.isOpened()) {
                mHandlerDL.open(ctrlBlock);
                final SurfaceTexture st = mUVCCameraViewDL.getSurfaceTexture();
                mHandlerDL.startPreview(new Surface(st));
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mCaptureButtonDL.setVisibility(View.VISIBLE);
                    }
                });
            } else if (!mHandlerDC.isOpened()) {
                mHandlerDC.open(ctrlBlock);
                final SurfaceTexture st = mUVCCameraViewDC.getSurfaceTexture();
                mHandlerDC.startPreview(new Surface(st));
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mCaptureButtonDC.setVisibility(View.VISIBLE);
                    }
                });
            }else if (!mHandlerDR.isOpened()) {
                mHandlerDR.open(ctrlBlock);
                final SurfaceTexture st = mUVCCameraViewDR.getSurfaceTexture();
                mHandlerDR.startPreview(new Surface(st));
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mCaptureButtonDR.setVisibility(View.VISIBLE);
                    }
                });
            }
        }

        @Override
        public void onDisconnect(final UsbDevice device, final UsbControlBlock ctrlBlock) {
            if (DEBUG) Log.v(TAG, "onDisconnect:" + device);
            if ((mHandlerL != null) && !mHandlerL.isEqual(device)) {
                queueEvent(new Runnable() {
                    @Override
                    public void run() {
                        mHandlerL.close();
                        if (mLeftPreviewSurface != null) {
                            mLeftPreviewSurface.release();
                            mLeftPreviewSurface = null;
                        }
                        setCameraButton();
                    }
                }, 0);
            } else if ((mHandlerR != null) && !mHandlerR.isEqual(device)) {
                queueEvent(new Runnable() {
                    @Override
                    public void run() {
                        mHandlerR.close();
                        if (mRightPreviewSurface != null) {
                            mRightPreviewSurface.release();
                            mRightPreviewSurface = null;
                        }
                        setCameraButton();
                    }
                }, 0);
            }else if ((mHandlerC != null) && !mHandlerC.isEqual(device)) {
                queueEvent(new Runnable() {
                    @Override
                    public void run() {
                        mHandlerC.close();
                        if (mCenterPreviewSurface != null) {
                            mCenterPreviewSurface.release();
                            mCenterPreviewSurface = null;
                        }
                        setCameraButton();
                    }
                }, 0);
            }else if ((mHandlerDL != null) && !mHandlerDL.isEqual(device)) {
                queueEvent(new Runnable() {
                    @Override
                    public void run() {
                        mHandlerDL.close();
                        if (mDownLeftPreviewSurface != null) {
                            mDownLeftPreviewSurface.release();
                            mDownLeftPreviewSurface = null;
                        }
                        setCameraButton();
                    }
                }, 0);
            } else if ((mHandlerDC != null) && !mHandlerDC.isEqual(device)) {
                queueEvent(new Runnable() {
                    @Override
                    public void run() {
                        mHandlerDC.close();
                        if (mDownCenterPreviewSurface != null) {
                            mDownCenterPreviewSurface.release();
                            mDownCenterPreviewSurface = null;
                        }
                        setCameraButton();
                    }
                }, 0);
            }else if ((mHandlerDR != null) && !mHandlerDR.isEqual(device)) {
                queueEvent(new Runnable() {
                    @Override
                    public void run() {
                        mHandlerDR.close();
                        if (mDownRightPreviewSurface != null) {
                            mDownRightPreviewSurface.release();
                            mDownRightPreviewSurface = null;
                        }
                        setCameraButton();
                    }
                }, 0);
            }
        }

        @Override
        public void onDettach(final UsbDevice device) {
            if (DEBUG) Log.v(TAG, "onDettach:" + device);
            Toast.makeText(MainActivity.this, "USB_DEVICE_DETACHED", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onCancel(final UsbDevice device) {
            if (DEBUG) Log.v(TAG, "onCancel:");
        }
    };

    /**
     * to access from CameraDialog
     *
     * @return
     */
    @Override
    public USBMonitor getUSBMonitor() {
        return mUSBMonitor;
    }

    @Override
    public void onDialogResult(boolean canceled) {
        if (canceled) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    setCameraButton();
                }
            }, 0);
        }
    }

    private void setCameraButton() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if ((mHandlerL != null) && !mHandlerL.isOpened() && (mCaptureButtonL != null)) {
                    mCaptureButtonL.setVisibility(View.INVISIBLE);
                }
                if ((mHandlerC != null) && !mHandlerC.isOpened() && (mCaptureButtonC != null)) {
                    mCaptureButtonC.setVisibility(View.INVISIBLE);
                }
                if ((mHandlerR != null) && !mHandlerR.isOpened() && (mCaptureButtonR != null)) {
                    mCaptureButtonR.setVisibility(View.INVISIBLE);
                }
                if ((mHandlerDL != null) && !mHandlerDL.isOpened() && (mCaptureButtonDL != null)) {
                    mCaptureButtonDL.setVisibility(View.INVISIBLE);
                }
                if ((mHandlerDC != null) && !mHandlerDC.isOpened() && (mCaptureButtonDC != null)) {
                    mCaptureButtonDC.setVisibility(View.INVISIBLE);
                }
                if ((mHandlerDR != null) && !mHandlerDR.isOpened() && (mCaptureButtonDR != null)) {
                    mCaptureButtonDR.setVisibility(View.INVISIBLE);
                }
            }
        }, 0);
    }
}
